home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / pascal / async4.zip / ASYNC.400
Text File  |  1988-02-03  |  21KB  |  566 lines

  1.  
  2. {
  3. This  version  of Michael Quinlan's ASYNC.INC is compatible  with
  4. IBM  PC  and  Compatibles.  It  gives  interrupt-driven  buffered
  5. communication  capabilities to Turbo programs written for the IBM
  6. PC.  It is heavily dependent on that hardware.
  7.  
  8. The Async_ITR routine was taken from N. Arley Dealey's Async4
  9. procedures, to make this set of routines work with version 4.0
  10. of turbo pascal.
  11.  
  12. The  following example routines are public domain  programs  that
  13. have  been uploaded to our Forum on CompuServe.  As a courtesy to
  14. our  users  that  do not have  immediate  access  to  CompuServe,
  15. Technical Support distributes these routines free of charge.
  16.  
  17. However,  because these routines are public domain programs,  not
  18. developed by Borland International,  we are unable to provide any
  19. technical support or assistance using these routines. If you need
  20. assistance   using   these   routines,    or   are   experiencing
  21. difficulties,  we  recommend  that you log  onto  CompuServe  and
  22. request  assistance  from the Forum members that developed  these
  23. routines.
  24. }
  25.  
  26. Unit Async;
  27.  
  28. Interface
  29.  
  30. Uses DOS;
  31. {--------------------------------------------------------------}
  32. {                        ASYNC.INC                             }
  33. {                                                              }
  34. {  Async Communication Routines                                }
  35. {  by Michael Quinlan                                          }
  36. {  with a bug fixed by Scott Herr                              }
  37. {  with Async_ISR update to 4.0 by N. Arley Dealey substituted }
  38. {                               by Keith Hawes                 }
  39. {  made PCjr-compatible by W. M. Miller                        }
  40. {  Highly dependent on the IBM PC and PC DOS 2.0               }
  41. {                                                              }
  42. {  based on the DUMBTERM program by CJ Dunford                 }
  43. {  in the January 1984                                         }
  44. {  issue of PC Tech Journal.                                   }
  45. {                                                              }
  46. {  Entry points:                                               }
  47. {--------------------------------------------------------------}
  48.  
  49. Procedure Async_Init;
  50. {--------------------------------------------------------------}
  51. {      Performs initialization.                                }
  52. {                                                              }
  53. {--------------------------------------------------------------}
  54.  
  55. function Async_Open(ComPort       : Word;
  56.                     BaudRate      : Word;
  57.                     Parity        : Char;
  58.                     WordSize      : Word;
  59.                     StopBits      : Word) : Boolean;
  60. {--------------------------------------------------------------}
  61. {   Sets up interrupt vector, initialize the COM port for      }
  62. {   processing, sets pointers to the buffer.  Returns FALSE    }
  63. {   if COM                                                     }
  64. {      port not installed.                                     }
  65. {--------------------------------------------------------------}
  66.  
  67. Function Async_Buffer_Check(var C : Char) : Boolean;
  68. {--------------------------------------------------------------}
  69. {      If a character is available, returns TRUE and moves the }
  70. {        character from the buffer to the parameter            }
  71. {      Otherwise, returns FALSE                                }
  72. {--------------------------------------------------------------}
  73.  
  74. Procedure Async_Send(C : Char);
  75. {--------------------------------------------------------------}
  76. {      Transmits the character.                                }
  77. {--------------------------------------------------------------}
  78.  
  79. Procedure Async_Send_String(S : string);
  80. {--------------------------------------------------------------}
  81. {      Calls Async_Send to send each character of S.           }
  82. {--------------------------------------------------------------}
  83.  
  84. Procedure Async_Close;
  85. {--------------------------------------------------------------}
  86. {    Turn off the COM port interrupts.                         }
  87. {    will see some really strange errors and have to re-boot.  }
  88. {--------------------------------------------------------------}
  89.  
  90. procedure Async_Change(BaudRate      : Word;
  91.                        Parity        : Char;
  92.                        WordSize      : Word;
  93.                        StopBits      : Word);
  94. {--------------------------------------------------------------}
  95. { change communication parameters "on the fly"                 }
  96. { you cannot use the BIOS routines because they drop DTR       }
  97. {--------------------------------------------------------------}
  98.  
  99. var
  100.   Async_Buffer_Overflow : Boolean;  { True if buffer overflow has happened }
  101.   Async_Buffer_Used     : Word;
  102.   Async_MaxBufferUsed   : Word;
  103.  
  104. Implementation
  105. { global declarations }
  106.  
  107. const
  108.   UART_THR = $00;
  109.       { offset from base of UART Registers for IBM PC }
  110.   UART_RBR = $00;
  111.   UART_IER = $01;
  112.   UART_IIR = $02;
  113.   UART_LCR = $03;
  114.   UART_MCR = $04;
  115.   UART_LSR = $05;
  116.   UART_MSR = $06;
  117.  
  118.   I8088_IMR = $21;
  119.        { port address of the Interrupt Mask Register }
  120.  
  121. const
  122.   Async_Buffer_Max       = 4095;
  123. var
  124.   Async_Interrupt_Save   : pointer;
  125.   Async_ExitProc_Save    : pointer;
  126.   Async_Buffer           : Array[0..Async_Buffer_Max] of char;
  127.   Async_Open_Flag        : Boolean;
  128.   Async_Port             : Word; { current Open port number (1 or 2)    }
  129.   Async_Base             : Word; { base for current open port           }
  130.   Async_Irq              : Word; { irq for current open port            }
  131.  
  132.     { Async_Buffer is empty if Head = Tail }
  133.  
  134.   Async_Buffer_Head     : Word;    { Locn in Async_Buffer to put next char }
  135.   Async_Buffer_Tail     : Word;    { Locn in Async_Buffer to get next char }
  136.   Async_Buffer_NewTail  : Word;
  137.  
  138.   Async_BIOS_Port_Table : Array[1..2] of Word absolute $40:0;
  139.     { This table is initialized by BIOS equipment determination
  140.     code at boot time to contain the base addresses for the
  141.     installed async adapters.  A value of 0 means "not in-
  142.     stalled." }
  143.  
  144. const
  145.   Async_Num_Bauds = 8;
  146.   Async_Baud_Table : array [1..Async_Num_Bauds] of record
  147.                                          Baud, Bits : Word
  148.                                       end
  149.                    = ((Baud:110;  Bits:$00),
  150.                       (Baud:150;  Bits:$20),
  151.                       (Baud:300;  Bits:$40),
  152.                       (Baud:600;  Bits:$60),
  153.                       (Baud:1200; Bits:$80),
  154.                       (Baud:2400; Bits:$A0),
  155.                       (Baud:4800; Bits:$C0),
  156.                       (Baud:9600; Bits:$E0));
  157.  
  158. procedure BIOS_RS232_Init(ComPort, ComParm : Word);
  159. { Issue Interrupt $14 to initialize the UART   }
  160. { Format of ComParm:  (From IBM Tech. Ref.)    }
  161. {                                              }
  162. { 7     6     5     4     3     2      1     0 }
  163. { --Baud Rate--     -Parity   StopBit  Word Len}
  164. {  000 =  110       x0 = None   0 = 1  10 = 7  }
  165. {  001 =  150       01 = Odd    1 = 2  11 = 8  }
  166. {  010 =  300       11 = Even                  }
  167. {  011 =  600                                  }
  168. {  100 = 1200                                  }
  169. {  101 = 2400                                  }
  170. {  110 = 4800                                  }
  171. {  111 = 9600                                  }
  172. {                                              }
  173.  
  174. var
  175.   Regs : registers;
  176. begin
  177.   with Regs do
  178.     begin
  179.       ax := ComParm and $00FF;  { AH=0; AL=ComParm }
  180.       dx := ComPort;
  181.       Intr($14, Regs)
  182.     end;
  183. end; { BIOS_RS232_Init }
  184.  
  185. {---------------------------------------------------------------------------}
  186. {                      ISR - Interrupt Service Routine                      }
  187. {---------------------------------------------------------------------------}
  188.  
  189. PROCEDURE Async_ISR ; INTERRUPT ;
  190. { Interrupt Service Routine }
  191. { Invoked when the USART has received a byte of data from the comm line     }
  192. { re-written 9/10/84 in machine language ; original source left as comments }
  193. { re-written 1987    to work under Turbo Pascal Version 4.0                 }
  194.  
  195. BEGIN { ISR }
  196.   inline(
  197.     $FB/                                { STI }
  198.  
  199.     { get the incoming character }
  200.     { Async_Buffer[Async_Buffer_Head] :=
  201.                                 CHR( port[Async_Base + DG1_USART_Data] ) ;  }
  202.     $8B/$16/Async_Base/                       { MOV DX,Base }
  203.     $EC/                                      { IN AL,DX }
  204.     $8B/$1E/Async_Buffer_Head/                { MOV BX,BufferHead }
  205.     $88/$87/Async_Buffer/                     { MOV Buffer[BX],AL }
  206.  
  207.     { Async_Buffer_NewHead := SUCC( Async_Buffer_Head ) ;           }
  208.     $43/                                      { INC BX }
  209.  
  210.     { IF Async_Buffer_NewHead > Async_Buffer_Max
  211.             THEN Async_Buffer_NewHead := 0 ; }
  212.     $81/$FB/Async_Buffer_Max/                 { CMP BX,BufferMax }
  213.     $7E/$02/                                  { JLE L001 }
  214.     $33/$DB/                                  { XOR BX,BX }
  215.  
  216.     { IF Async_Buffer_NewHead = Async_Buffer_Tail THEN Overflow := TRUE }
  217.     {L001:}
  218.     $3B/$1E/Async_Buffer_Tail/                { CMP BX,Async_Buffer_Tail }
  219.     $75/$08/                                  { JNE L002 }
  220.     $C6/$06/Async_Buffer_Overflow/$01/        { MOV Overflow,1 }
  221.     $90/                                      { NOP generated by assembler }
  222.     $EB/$16/                                  { JMP SHORT L003 }
  223.     { ELSE BEGIN                                                    }
  224.     {   Async_Buffer_Head := Async_Buffer_NewHead ;                 }
  225.     {   Async_Buffer_Used  := SUCC( Async_Buffer_Used ) ;           }
  226.     {   IF Async_Buffer_Used > Async_MaxBufferUsed THEN             }
  227.     {     Async_MaxBufferUsed := Async_BufferUsed                   }
  228.     {   END ;                                                       }
  229.     {L002:}
  230.     $89/$1E/Async_Buffer_Head/                { MOV BufferHead,BX }
  231.       $FF/$06/Async_Buffer_Used/              { INC Async_BufferUsed }
  232.       $8B/$1E/Async_Buffer_Used/              { MOV BX,Async_BufferUsed }
  233.       $3B/$1E/Async_MaxBufferUsed/            { CMP BX,Async_MaxBufferUsed }
  234.       $7E/$04/                                { JLE L003 }
  235.       $89/$1E/Async_MaxBufferUsed/            { MOV Async_MaxBufferUsed,BX }
  236.     {L003:}
  237.  
  238.     $FA/                                      { CLI }
  239.  
  240.     { issue non-specific EOI }
  241.     { port[$20] := $20 ;                                                }
  242.     $B0/$20/                                  { MOV AL,20h }
  243.     $E6/$20                                   { OUT 20h,AL }
  244.     )
  245.   END { Async_ISR } ;
  246.  
  247.  
  248. procedure Async_Init;
  249. { initialize variables }
  250. begin
  251.   Async_Open_Flag := FALSE;
  252.   Async_Buffer_Overflow := FALSE;
  253.   Async_Buffer_Used := 0;
  254.   Async_MaxBufferUsed := 0;
  255. end; { Async_Init }
  256.  
  257. procedure Async_Close;
  258. { reset the interrupt system when UART interrupts
  259.   no longer needed }
  260. var
  261.   i, m : Word;
  262. begin
  263.   if Async_Open_Flag then
  264.     begin
  265.  
  266.       { disable the IRQ on the 8259 }
  267.       Inline($FA);                              { disable interrupts }
  268.       i := Port[I8088_IMR];        { get the interrupt mask register }
  269.       m := 1 shl Async_Irq;         { set mask to turn off interrupt }
  270.       Port[I8088_IMR] := i or m;
  271.  
  272.       { disable the 8250 data ready interrupt }
  273.       Port[UART_IER + Async_Base] := 0;
  274.  
  275.       { disable OUT2 on the 8250 }
  276.       Port[UART_MCR + Async_Base] := 0;
  277.       Inline($FB);         { enable interrupts }
  278.  
  279.       { re-initialize our data areas so
  280.         we know the port is closed }
  281.       Async_Open_Flag := FALSE;
  282.  
  283. {Version 4 support by Keith Hawes next 2 lines}
  284.       SetIntVec( Async_IRQ + 8, Async_Interrupt_Save ); {restore old interupt}
  285.       ExitProc := Async_ExitProc_Save;                {restore ExirProc chain}
  286.     end
  287. end; { Async_Close }
  288.  
  289. function Async_Open(ComPort       : Word;
  290.                     BaudRate      : Word;
  291.                     Parity        : Char;
  292.                     WordSize      : Word;
  293.                     StopBits      : Word) : Boolean;
  294. { open a communications port }
  295. var
  296.   ComParm : Word;
  297.   i, m : Word;
  298. begin
  299.   if Async_Open_Flag then Async_Close;
  300.  
  301.   if (ComPort = 2) and (Async_BIOS_Port_Table[2] <> 0) then
  302.     Async_Port := 2
  303.   else
  304.     Async_Port := 1;  { default to COM1 }
  305.   Async_Base := Async_BIOS_Port_Table[Async_Port];
  306.   Async_Irq := Hi(Async_Base) + 1;
  307.  
  308.   if (Port[UART_IIR + Async_Base] and $00F8) <> 0 then
  309.     Async_Open := FALSE
  310.   else
  311.     begin
  312.       Async_Buffer_Head := 0;
  313.       Async_Buffer_Tail := 0;
  314.       Async_Buffer_Overflow := FALSE;
  315.  
  316.   { Build the ComParm for RS232_Init }
  317.   { See Technical Reference Manual for description }
  318.  
  319.       ComParm := $0000;
  320.  
  321.   { Set up the bits for the baud rate }
  322.       i := 0;
  323.       repeat
  324.         i := i + 1
  325.       until (Async_Baud_Table[i].Baud = BaudRate)
  326.               or (i = Async_Num_Bauds);
  327.       ComParm := ComParm or Async_Baud_Table[i].Bits;
  328.  
  329.       if Parity in ['E', 'e'] then ComParm := ComParm or $0018
  330.       else if Parity in ['O', 'o'] then
  331.            ComParm := ComParm or $0008
  332.       else ComParm := ComParm or $0000;  { default to No parity }
  333.       if WordSize = 7 then ComParm := ComParm or $0002
  334.       else ComParm := ComParm or $0003;  { default to 8 data bits }
  335.  
  336.       if StopBits = 2 then ComParm := ComParm or $0004
  337.       else ComParm := ComParm or $0000;  { default to 1 stop bit }
  338.  
  339.       { use the BIOS COM port initialization routine
  340.         to save typing the code }
  341.  
  342.       BIOS_RS232_Init(Async_Port - 1, ComParm);
  343.       GetIntVec( Async_Irq + 8, Async_Interrupt_Save ); {Version 4 support KH}
  344.       Async_ExitProc_Save := ExitProc;       {Version 4 support by Keith Hawes}
  345.       ExitProc := @Async_Close;              {Version 4 support by Keith Hawes}
  346.       SetIntVec( Async_Irq + 8, @Async_Isr );{Version 4 support by Keith Hawes}
  347.  
  348. { read the RBR and reset any possible pending error conditions
  349.   first turn off the Divisor Access Latch Bit to allow
  350.   access to RBR, etc. }
  351.  
  352.       Inline($FA);  { disable interrupts }
  353.  
  354.       Port[UART_LCR + Async_Base] :=
  355.               Port[UART_LCR + Async_Base] and $7F;
  356.       { read the Line Status Register to reset any
  357.         errors it indicates }
  358.       i := Port[UART_LSR + Async_Base];
  359.       { read the Receiver Buffer Register in case
  360.         it contains a character }
  361.       i := Port[UART_RBR + Async_Base];
  362.  
  363.       { enable the irq on the 8259 controller }
  364.       i := Port[I8088_IMR];  { get the interrupt mask register }
  365.       m := (1 shl Async_Irq) xor $00FF;
  366.       Port[I8088_IMR] := i and m;
  367.  
  368.       { enable the data ready interrupt on the 8250 }
  369.       Port[UART_IER + Async_Base] := $01;
  370.       { enable data ready interrupt }
  371.  
  372.       { enable OUT2 on 8250 }
  373.       i := Port[UART_MCR + Async_Base];
  374.       Port[UART_MCR + Async_Base] := i or $08;
  375.       Inline($FB); { enable interrupts }
  376.       Async_Open_Flag := TRUE;  { bug fix by Scott Herr }
  377.       Async_Open := TRUE
  378.     end;
  379. end; { Async_Open }
  380.  
  381. function Async_Buffer_Check(var C : Char) : Boolean;
  382. { see if a character has been received; return it if yes }
  383. begin
  384.   if Async_Buffer_Head = Async_Buffer_Tail then
  385.     Async_Buffer_Check := FALSE
  386.   else
  387.     begin
  388.       C := Async_Buffer[Async_Buffer_Tail];
  389.       Async_Buffer_Tail := Async_Buffer_Tail + 1;
  390.       if Async_Buffer_Tail > Async_Buffer_Max then
  391.         Async_Buffer_Tail := 0;
  392.       Async_Buffer_Used := Async_Buffer_Used - 1;
  393.       Async_Buffer_Check := TRUE
  394.     end
  395. end; { Async_Buffer_Check }
  396.  
  397. procedure Async_Send(C : Char);
  398. { transmit a character }
  399. var
  400.   i, m, counter : Word;
  401. begin
  402.   Port[UART_MCR + Async_Base] := $0B; { turn on OUT2, DTR, and RTS }
  403.  
  404.   { wait for CTS }
  405.   counter := MaxInt;
  406.   while (counter <> 0) and
  407.         ((Port[UART_MSR + Async_Base] and $10) = 0) do
  408.     counter := counter - 1;
  409.  
  410.   { wait for Transmit Hold Register Empty (THRE) }
  411.   if counter <> 0 then counter := MaxInt;
  412.   while (counter <> 0) and
  413.         ((Port[UART_LSR + Async_Base] and $20) = 0) do
  414.     counter := counter - 1;
  415.   if counter <> 0 then
  416.     begin
  417.       { send the character }
  418.       Inline($FA); { disable interrupts }
  419.       Port[UART_THR + Async_Base] := Ord(C);
  420.       Inline($FB) { enable interrupts }
  421.     end
  422.   else
  423.     writeln('<<<TIMEOUT>>>');
  424. end; { Async_Send }
  425.  
  426. procedure Async_Send_String(S : String);
  427. { transmit a string }
  428. var
  429.   i : Word;
  430. begin
  431.   for i := 1 to length(S) do
  432.     Async_Send(S[i])
  433. end; { Async_Send_String }
  434.  
  435. procedure Async_Change(BaudRate      : Word;
  436.                        Parity        : Char;
  437.                        WordSize      : Word;
  438.                        StopBits      : Word);
  439. { change communication parameters "on the fly" }
  440. { you cannot use the BIOS routines because they drop DTR }
  441.  
  442. const num_bauds = 15;
  443.     divisor_table : array [1..num_bauds] of record
  444.                                             baud, divisor : Word
  445.                                           end
  446.        = ((baud:50;  divisor:2304),
  447.           (baud:75;  divisor:1536),
  448.           (baud:110; divisor:1047),
  449.           (baud:134; divisor:857),
  450.           (baud:150; divisor:768),
  451.           (baud:300; divisor:384),
  452.           (baud:600; divisor:192),
  453.           (baud:1200; divisor:96),
  454.           (baud:1800; divisor:64),
  455.           (baud:2000; divisor:58),
  456.           (baud:2400; divisor:48),
  457.           (baud:3600; divisor:32),
  458.           (baud:4800; divisor:24),
  459.           (baud:7200; divisor:16),
  460.           (baud:9600; divisor:12));
  461.  
  462. var i : Word;
  463.     dv  : Word;
  464.     lcr : Word;
  465. begin
  466.  
  467.   { Build the Line Control Register and find
  468.     the divisor (for the baud rate) }
  469.  
  470.   { Set up the divisor for the baud rate }
  471.   i := 0;
  472.   repeat
  473.     i := i + 1
  474.   until (Divisor_Table[i].Baud = BaudRate) or (i = Num_Bauds);
  475.   dv  := Divisor_Table[i].divisor;
  476.  
  477.   lcr := 0;
  478.   case Parity of
  479.     'E' : lcr := lcr or $18;  { even parity }
  480.     'O' : lcr := lcr or $08;  { odd parity }
  481.     'N' : lcr := lcr or $00;  { no parity }
  482.     'M' : lcr := lcr or $28;  { Mark parity }
  483.     'S' : lcr := lcr or $38;  { Space parity }
  484.   else
  485.     lcr := lcr or $00;  { default to no parity }
  486.   end;
  487.  
  488.   case WordSize of
  489.     5 : lcr := lcr or $00;
  490.     6 : lcr := lcr or $01;
  491.     7 : lcr := lcr or $02;
  492.     8 : lcr := lcr or $03;
  493.   else
  494.     lcr := lcr or $03;  { default to 8 data bits }
  495.   end;
  496.  
  497.   if StopBits = 2 then lcr := lcr or $04
  498.   else lcr := lcr or $00;  { default to 1 stop bit }
  499.  
  500.   lcr := lcr and $7F;   { make certain the DLAB is off }
  501.  
  502.   Inline($FA);  { disable interrupts }
  503.  
  504.   { turn on DLAB to access the divisor }
  505.   Port[UART_LCR + Async_Base] := Port[UART_LCR + Async_Base] or $80;
  506.  
  507.   { set the divisor }
  508.   Port[Async_Base] := Lo(dv);
  509.   Port[Async_Base + 1] := Hi(dv);
  510.  
  511.   { turn off the DLAB and set the new comm. parameters }
  512.   Port[UART_LCR + Async_Base] := lcr;
  513.  
  514.   Inline($FB);  { enable interrupts }
  515.  
  516. end; { Async_Change }
  517. end.
  518.  
  519. *****************************************************************************
  520.   Test Program.... place in a separate file and compile with the Make option
  521.  
  522. program tty;
  523. uses crt,async;
  524. var
  525.   c : char;
  526.  
  527. begin
  528.   Async_Init;  { initialize variables }
  529.   if not Async_Open(2, 1200, 'E', 7, 1) then  {open communications port}
  530.     begin
  531.       writeln('**ERROR: Async_Open failed');
  532.       halt
  533.     end;
  534.  
  535.   writeln('TTY Emulation begins now...');
  536.   writeln('Press ESC key to terminate...');
  537.  
  538.   repeat
  539.     if Async_Buffer_Check(c) then
  540.       case c of
  541.         #000 : ;  { strip incoming nulls }
  542.         #010 : ;  { strip incoming line feeds }
  543.         #012 : ClrScr;  { clear screen on a form feed }
  544.         #013 : Writeln  { handle carriage return as CR/LF }
  545.       else
  546.         write(c)  { else write incoming char to the screen }
  547.       end; { case }
  548.     if KeyPressed then
  549.       begin
  550.         c := readkey;
  551.         if c = #027 then  { Trap Esc Key }
  552.           begin
  553.             Async_Close;   { reset the interrupt system, etc. }
  554.             Writeln('End of TTY Emulation...');
  555.             halt;          { terminate the program }
  556.           end
  557.         else
  558.           Async_Send(c)
  559.       end;
  560.   until FALSE;
  561. end.
  562.  
  563. *****************************************************************************
  564.  
  565.  
  566.